/****************************************************************
 ** Product  :   HP Subscriber Network Application Policy
 ** Module   :   gr-manager
 ** Date: Apr 10, 2015               
 ** Author: Huang xiaoji
 ** (C) Copyright 2015, Hewlett-Packard Company, All Rights Reserved.
 ** This software is the proprietary information of HP, Inc.  
 ** Use is subject to license terms.
 *****************************************************************
 ** Description:   
 **     Export the session servers config to file.
 **    
 ****************************************************************/
package com.hp.snap.gr.action.impl;

import com.hp.snap.gr.exception.ActionException;
import com.hp.snap.gr.model.jaxb.ActionType;
import com.hp.snap.gr.utils.CmdUtils;
import com.hp.snap.gr.utils.Constants;
import com.hp.snap.gr.utils.GRMConfigManager;
import com.hp.snap.gr.utils.Utils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils;

import java.io.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Export the session servers config to file. Export whole session server configuration to deployment.config,
 * NMESchema.config,SNMESchema.config and SNAP.config files respectively in /tmp/exportSessionServer folder. Read the
 * deployment.config file and matched with SS_Type configuration item to get each session server configuration and save
 * to specified config file in /work/exportTarget/SessionServer folder. And also copy NMESchema.config,SNMESchema.config
 * and SNAP.config files to /work/exportTarget/SessionServer folder.
 * 
 * <P>
 * Version: 3.3 <br>
 * Author: Huang xiaoji Date: Apr 10, 2015
 * 
 * </P>
 * 
 **/
public class ExportSessionServerActionHandler extends AbstractActionHandler {
    //0 -- content
    //1 -- filter success title
    //2 -- filter failure title
    private static final Pattern SESSION_SERVER_TITLE = Pattern.compile("^\\s*\\[/deployment/([^/]+)(?:/([^/]+))?(?:.*)]\\s*$");
    private static final Pattern NODE_PATH = Pattern.compile("\\s*\\[([^\\]]+)\\]");
    private static Logger logger = LoggerFactory.getLogger(ExportSessionServerActionHandler.class);

    @Override
    public void execute(ActionType action, Map<String, Object> actionPlanInputs, Map<String, Object> actionPlanOutputs) throws ActionException {
        //export whole session server configuration
        File exportSSDir = new File(Constants.USER_GR_TEMP, "exportSessionServer");
        try {
            boolean isSecure = GRMConfigManager.isSecure();
            String eIUMHome = GRMConfigManager.getEIUMHome();
            String username = GRMConfigManager.getUsername();
            String password = GRMConfigManager.getPassword();

            String exportCmd = eIUMHome + File.separator + "bin" + File.separator + "saveconfig";

            if (isSecure) {
                exportCmd += " -login " + username + " -pw " + password;
            }
            exportCmd += " -dir " + exportSSDir.getAbsolutePath();

            if (logger.isTraceEnabled()) {
                logger.trace("going to execute command: {}", CmdUtils.printableCommand(exportCmd));
            }
            try {
                CmdUtils.runCmd(exportCmd, true, password);
            } catch (Exception e) {
                logger.error("execute command failed, {}", e.getMessage(), e);
                throw new ActionException("export session server configurations failed", e);
            }

            File deploymentSource = new File(exportSSDir, "deployment.config");
            if (!deploymentSource.exists()) {
                logger.error("deployment source file cant found, file: {}", deploymentSource.getAbsoluteFile());
                throw new ActionException("Can't find source deployment file");
            }
            File nmeSchemaSource = new File(exportSSDir, "NMESchema.config");
            if (!nmeSchemaSource.exists()) {
                logger.error("NME schema source file cant found, file: {}", nmeSchemaSource.getAbsoluteFile());
                throw new ActionException("Can't find source NME schema file");
            }
            File snmeSchemaSource = new File(exportSSDir, "SNMESchema.config");
            if (!snmeSchemaSource.exists()) {
                logger.error("SNME schema source file cant found, file: {}", snmeSchemaSource.getAbsoluteFile());
                throw new ActionException("Can't find source SNME schema file");
            }

            File nmeSNAP = new File(exportSSDir, "SNAP.config");
            if (!nmeSNAP.exists()) {
                logger.error("SNAP schema source file cant found, file: {}", nmeSNAP.getAbsoluteFile());
                throw new ActionException("Can't find source SNAP schema file");
            }

            File exportRoot = new File(Constants.USER_GR_WORK, "exportTarget");
            exportRoot.mkdirs();
            File exportSSTarget = new File(exportRoot, "SessionServer");
            exportSSTarget.mkdir();

            String[] types = Utils.getMultiConfigurationValue(Constants.CFG_GRG_SESSION_SERVER, Constants.CFG_GRG_SESSION_SERVER_TYPES);
            if (types != null && ArrayUtils.isNotEmpty(types)) {
                Set<String> filterString = new HashSet<String>();
//                filterString.add("Properties");
//                filterString.add("Defaults");
//                filterString.add("Environment");
//only export session server configuration
                //SS_TYPES=<SESSION_SERVER_TYPE>, <SESSION_SERVER_NMAE1>|<SESSION_SERVER_NAME2>
                Pattern pattern = Pattern.compile("([^=,\\|\\s]+)");
                for (String type : types) {
                    Matcher m = pattern.matcher(type);
                    int i = 0;
                    while (m.find()) {
                        if (i > 0) {
                            filterString.add(m.group(1));
                        }
                        i++;
                    }
                }
                if (logger.isTraceEnabled()) {
                    logger.trace("session server filter pattern: {}", filterString);
                }

                generateConfigFile(deploymentSource, exportSSTarget, filterString);
            }

            try {
                copyFile(nmeSchemaSource, new File(exportSSTarget, nmeSchemaSource.getName()));
                copyFile(snmeSchemaSource, new File(exportSSTarget, snmeSchemaSource.getName()));
                copyFile(nmeSNAP, new File(exportSSTarget, nmeSNAP.getName()));
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
                throw new ActionException(e.getMessage(), e);
            }
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("will not delete export dir for TRACE");
            } else {
                FileSystemUtils.deleteRecursively(exportSSDir);
            }
        }
    }

    private void generateConfigFile(File sourceFile, File destinationRoot, Set<String> filterString) throws ActionException {
        BufferedReader in = null;
        FileOutputStream out = null;
        try {
            in = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile)));
//            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destinationFile)));
            out = null;
            Set<String> nodes = new HashSet<String>();
            boolean flag = false;
            while (in.ready()) {
                String line = in.readLine();

                int rs = filter(filterString, line);
                if (rs == 1) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("configuration block {} will copy to new configuration file", line);
                    }
                    flag = true;
                    String[] vars = StringUtils.split(line, "/");
                    if (vars.length == 4) {
                        if (out != null) {
                            out.flush();
                            out.close();
                        }
                        out = new FileOutputStream(new File(destinationRoot, vars[2] + "." + vars[3].substring(0, vars[3].length() - 1) + ".config"));
                    }
//                    fillUpNodePath(nodes, line, out);
                } else if (rs == 2) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("configuration block {} will ignore", line);
                    }
                    flag = false;
                }
                if (flag && out != null) {
                    out.write(line.getBytes());
                    out.write(Constants.CRLF);
                }
            }
            if (out != null)
                out.flush();
        } catch (FileNotFoundException e) {
            logger.error(e.getMessage(), e);
            throw new ActionException(e.getMessage(), e);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new ActionException(e.getMessage(), e);
        } finally {
            try {
                if (in != null) in.close();
                if (out != null) out.close();
            } catch (IOException e) {
                logger.error("close file failed, " + e.getMessage(), e);
                throw new ActionException("close configuration file failed", e);
            }
        }
        if (logger.isTraceEnabled()) {
            logger.trace("session server backup successful.");
        }
    }

    private void fillUpNodePath(Set<String> nodes, String line, BufferedWriter out) throws IOException {
        Matcher m = NODE_PATH.matcher(line);
        if (m.find()) {
            String path = m.group(1);
            fillUpNodePath0(nodes, path, out);
            nodes.add(path);
        } else {
            logger.debug("configuration line {} is not a configuration node", line);
        }
    }

    private void fillUpNodePath0(Set<String> nodes, String path, BufferedWriter out) throws IOException {
        String parent = getParentPath(path);
        if (parent != null) {
            if (!nodes.contains(parent)) {
                fillUpNodePath0(nodes, parent, out);
                out.write("[" + parent + "]");
                out.newLine();
                nodes.add(parent);
            }
        }
    }

    private String getParentPath(String path) {
        int index = path.lastIndexOf("/");
        if (index > 2) {
            return path.substring(0, index);
        } else {
            return null;
        }
    }

    private int filter(Set<String> filterString, String line) {
        Matcher m = SESSION_SERVER_TITLE.matcher(line);
        if (m.find()) {
            String compareString = null;

            compareString = m.group(2);
            if (compareString == null) {
                compareString = m.group(1);
            }
            for (String filter : filterString) {
                if (filter.endsWith("*")) {
                    filter = filter.substring(0, filter.length() - 1);
                    if (compareString.startsWith(filter)) {
                        return 1;
                    }
                } else if (filter.equals(compareString)) {
                    return 1;
                }
            }
            return 2;
        } else {
            return 0;
        }
    }

    private void copyFile(File source, File destination) throws IOException {
        FileCopyUtils.copy(source, destination);

        if (logger.isTraceEnabled()) {
            logger.trace("file {} copy to {} successful.", source.getAbsolutePath(), destination.getAbsolutePath());
        }
    }
}
